package com.tbit.uqbike.client.repository.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.tbit.uqbike.client.common.Pair;
import com.tbit.uqbike.client.dao.log.EventRuleDao;
import com.tbit.uqbike.client.dao.log.OrderEventDao;
import com.tbit.uqbike.client.dao.log.OrderEventDetailDao;
import com.tbit.uqbike.client.ordertrace.*;
import com.tbit.uqbike.client.ordertrace.validation.ValidatorFactory;
import com.tbit.uqbike.client.pojo.ordertrace.EventRule;
import com.tbit.uqbike.client.pojo.ordertrace.OrderEvent;
import com.tbit.uqbike.client.pojo.ordertrace.OrderEventDetail;
import com.tbit.uqbike.client.repository.OrderEventRepository;
import com.tbit.uqbike.client.service.ordertrace.OrderEventBO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 订单事件存储实现
 *
 *
 * @author <a href="mailto:chen.chaoxiong@tbit.com.cn">ChenChaoXiong</a>
 * @since 2024/7/4
 */
@Repository
@Slf4j
public class OrderEventRepositoryImpl implements OrderEventRepository {

    @Resource
    private OrderEventDao orderEventDao;

    @Resource
    private OrderEventDetailDao orderEventDetailDao;

    @Resource
    private EventRuleDao eventRuleDao;

    @Override
    public int saveOrUpdateOrderEvent(OrderEventBO orderEvent) {
        Long id = orderEvent.getId();
        if (id == null) {
            return svaeOrderEvent(orderEvent);
        } else {
            return updateOrderEvent(orderEvent);
        }
    }

    @Override
    public OrderEventBO queryOrderEventByTraceId(String traceId) {
        OrderEvent orderEvent = orderEventDao.selectByTraceId(traceId);
        List<EventRule> eventRules = queryAllEventRule();
        Table<OrderStage, Event, OrderStage> transitions = createTransitionTable(eventRules);
        Map<Pair<OrderStage, Event>, TransitionValidator> validators = createValidatorMap(eventRules);
        StateTransitionComponent stateTransitionComponent = new StateTransitionComponentImpl(transitions, validators);
        return OrderEventBO.fromOrderEvent(orderEvent,stateTransitionComponent);
    }

    @Override
    public List<EventRule> queryAllEventRule() {
        return Optional.ofNullable(eventRuleDao.selectAll()).orElse(CollectionUtil.newArrayList());
    }

    @Override
    public OrderStateMachine createOrderStateMachine(String traceId) {
        List<EventRule> eventRules = queryAllEventRule();
        Table<OrderStage, Event, OrderStage> transitions = createTransitionTable(eventRules);
        Map<Pair<OrderStage, Event>, TransitionValidator> validators = createValidatorMap(eventRules);
        StateTransitionComponent stateTransitionComponent = new StateTransitionComponentImpl(transitions, validators);
        OrderStateMachine orderStateMachine = OrderStateMachine.create(OrderStage.init(),stateTransitionComponent,traceId);
        return orderStateMachine;
    }

    private int svaeOrderEvent(OrderEventBO orderEventBO) {

        int orderEventInsertRes = 0;
        int detailInsertRes = 0;

        OrderEvent orderEvent = orderEventBO.toOrderEvent();
        orderEvent.setCreateTime(LocalDateTime.now());
        orderEvent.setUpdateTime(LocalDateTime.now());
        orderEventInsertRes = orderEventDao.insert(orderEvent);
        log.info("save orderEvent traceId:{} resut:{}", orderEvent.getTraceId(),orderEventInsertRes);

        List<OrderEventDetail> orderEventDetails = orderEventBO.toOrderEventDetailList();
        if (CollectionUtil.isNotEmpty(orderEventDetails)) {
            detailInsertRes = orderEventDetailDao.insertBatch(orderEventDetails);
            log.info("save orderEventDetail traceId:{} resut:{}", orderEvent.getTraceId(),detailInsertRes);
        }

        return orderEventInsertRes + detailInsertRes;

    }

    private int updateOrderEvent(OrderEventBO orderEventBO) {
        int orderEventUpdateRes = 0;
        int detailInsertRes = 0;
        OrderEvent orderEvent = orderEventBO.toOrderEvent();
        orderEvent.setUpdateTime(LocalDateTime.now());
        orderEventUpdateRes = orderEventDao.updateById(orderEvent);
        log.info("update orderEvent traceId:{} resut:{}", orderEvent.getTraceId(),orderEventUpdateRes);

        List<OrderEventDetail> orderEventDetails = orderEventBO.toOrderEventDetailList();
        orderEventDetails.forEach(orderEventDetail -> {
            orderEventDetail.setCreateTime(LocalDateTime.now());
            orderEventDetail.setUpdateTime(LocalDateTime.now());
        });
        if (CollectionUtil.isNotEmpty(orderEventDetails)) {
            detailInsertRes = orderEventDetailDao.insertBatch(orderEventDetails);
            log.info("update orderEventDetail traceId:{} resut:{}", orderEvent.getTraceId(),detailInsertRes);
        }

        return orderEventUpdateRes + detailInsertRes;
    }


    private Table<OrderStage, Event, OrderStage> createTransitionTable(List<EventRule> eventRules) {
        Table<OrderStage, Event, OrderStage> transitions = HashBasedTable.create();
        for (EventRule rule : eventRules) {
            OrderStage beforeStage = new OrderStage(rule.getBeforeStage());
            OrderStage afterStage = new OrderStage(rule.getAfterStage());
            Event event = new Event();
            event.setEventType(rule.getEventType());
            transitions.put(beforeStage, event, afterStage);
        }
        return transitions;
    }

    private Map<Pair<OrderStage, Event>, TransitionValidator> createValidatorMap(List<EventRule> eventRules) {
        Map<Pair<OrderStage, Event>, TransitionValidator> validators = new HashMap<>();
        for (EventRule rule : eventRules) {
            OrderStage beforeStage = new OrderStage(rule.getBeforeStage());
            Event event = new Event();
            event.setEventType(rule.getEventType());
            Pair<OrderStage, Event> pair = new Pair<>(beforeStage, event);
            String validationRule = rule.getValidationRule();
            TransitionValidator validator = ValidatorFactory.createValidator(rule.getRuleType(), validationRule);
            validators.put(pair, validator);
        }
        return validators;
    }
}
